﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data;
using StyleMVVM.LanguageExtensions;
using StyleMVVM.Logging;
using StyleMVVM.Validation.Impl.Fluent.Operators;
#if NETFX_CORE
using Windows.ApplicationModel.Resources;

#endif

namespace StyleMVVM.Validation.Impl.Fluent
{
	public class FluentRule<T> : IFluentRule<T>, ISyncValidationRule
	{
		private readonly List<string> dependentProperties = new List<string>();
		private readonly List<string> standards = new List<string>();
		private Action<IRuleExecutionContext> validateAction;
		private FluentRuleBuilder<T> ruleBuilder = new FluentRuleBuilder<T>();
		private readonly IReflectionService reflectionService;
		private readonly string namespaceVar;

		#region IValidationRule

		public FluentRule(IReflectionService reflectionService, string name)
		{
			this.reflectionService = reflectionService;
			namespaceVar = "Fluent|" + typeof(T).FullName;

			Name = name;
		}

		public string Namespace
		{
			get { return namespaceVar; }
		}

		public string DisplayName { get; private set; }

		public string Name { get; private set; }

		public IEnumerable<string> DependentProperties()
		{
			return dependentProperties;
		}

		public IEnumerable<string> Standards()
		{
			return standards;
		}

		public void Validate(IRuleExecutionContext context)
		{
			try
			{
				validateAction(context);
			}
			catch (Exception exp)
			{
				Logger.Error("Exception thrown while executing Validation Rule", typeof(FluentRule<T>).FullName, exp);
			}
		}

		#endregion

		#region IFluentRule<T>

		public IFluentRule<T> If
		{
			get
			{
				ruleBuilder.SetIf();

				return this;
			}
		}

		public IFluentRule<T> Then
		{
			get
			{
				ruleBuilder.SetThen();

				return this;
			}
		}

		public IFluentRule<T> Else
		{
			get
			{
				ruleBuilder.SetElse();

				return this;
			}
		}

		public IFluentRule<T> And
		{
			get
			{
				ruleBuilder.SetAnd();

				return this;
			}
		}

		public IFluentRule<T> Or
		{
			get
			{
				ruleBuilder.SetOr();

				return this;
			}
		}

		public IFluentRule<T> When
		{
			get
			{
				ruleBuilder.SetWhen();

				return this;
			}
		}

		public IFluentRule<T> Unless
		{
			get
			{
				ruleBuilder.SetUnless();

				return this;
			}
		}

		public IFluentRule<T> GreaterThan
		{
			get
			{
				ruleBuilder.SetOperator(new GreaterThanOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> GreaterThanOrEqualTo
		{
			get
			{
				ruleBuilder.SetOperator(new GreaterThanOrEqualOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> LessThan
		{
			get
			{
				ruleBuilder.SetOperator(new LessThanOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> LessThanOrEqualTo
		{
			get
			{
				ruleBuilder.SetOperator(new LessThanOrEqualOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> EqualTo
		{
			get
			{
				ruleBuilder.SetOperator(new EqualToOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> NotEqualTo
		{
			get
			{
				ruleBuilder.SetOperator(new NotEqualToOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> Contains
		{
			get
			{
				ruleBuilder.SetOperator(new ContainsOperator<T>());

				return this;
			}
		}

		public IFluentRule<T> IsNull()
		{
			ruleBuilder.SetOperator(new IsNullOperator<T>());

			return this;
		}

		public IFluentRule<T> IsNotNull()
		{
			ruleBuilder.SetOperator(new IsNotNullOperator<T>());

			return this;
		}

		public IFluentRule<T> IsEmpty()
		{
			ruleBuilder.SetOperator(new IsEmptyOperator<T>());

			return this;
		}

		public IFluentRule<T> IsNotEmpty()
		{
			ruleBuilder.SetOperator(new IsNotEmptyOperator<T>());

			return this;
		}

		public IFluentRule<T> IsTrue()
		{
			ruleBuilder.SetOperator(new IsTrueOperator<T>());

			return this;
		}

		public IFluentRule<T> IsFalse()
		{
			ruleBuilder.SetOperator(new IsFalseOperator<T>());

			return this;
		}

		public IFluentRule<T> IsTrueOrNull()
		{
			ruleBuilder.SetOperator(new IsTrueOrNullOperator<T>());

			return this;
		}

		public IFluentRule<T> IsFalseOrNull()
		{
			ruleBuilder.SetOperator(new IsFalseOrNullOperator<T>());

			return this;
		}

		public IFluentRule<T> IsRequired()
		{
			ruleBuilder.SetOperator(new IsRequiredOperator<T>());

			return this;
		}

		public IFluentRule<T> Property<TProp>(Expression<Func<T, TProp>> propertyFunc, string displayName = null)
		{
			string propertyName = reflectionService.GetPropertyAccessPath(propertyFunc);

			ruleBuilder.SetValueProvider(new FluentPropertyValueProvider<T>(propertyName));

			dependentProperties.Add(propertyName);

			return this;
		}

		public IFluentRule<T> Validator<TProp>(Expression<Func<T, TProp>> propertyFunc,
		                                       IFluentValidator<TProp> validator,
		                                       string displayName = null)
		{
			return this;
		}

		public IFluentRule<T> Method<TReturn>(Expression<Func<T, TReturn>> propertyFunc)
		{
			return this;
		}

		public IIntPropertyOperator<T> IntProperty<TProp>(Expression<Func<T, TProp>> propertyFunc)
		{
			string propertyName = reflectionService.GetPropertyAccessPath(propertyFunc);

			FluentIntProperty<T> returnValue = new FluentIntProperty<T>(this, propertyName);

			ruleBuilder.SetValueProvider(returnValue);

			dependentProperties.Add(propertyName);

			return returnValue;
		}

		public IDoublePropertyOperator<T> DoubleProperty<TProp>(Expression<Func<T, TProp>> propertyFunc)
		{
			string propertyName = reflectionService.GetPropertyAccessPath(propertyFunc);

			FluentDoubleProperty<T> returnValue = new FluentDoubleProperty<T>(this, propertyName);

			ruleBuilder.SetValueProvider(returnValue);

			dependentProperties.Add(propertyName);

			return returnValue;
		}

		public IStringPropertyOperator<T> StringProperty<TProp>(Expression<Func<T, TProp>> propertyFunc)
		{
			string propertyName = reflectionService.GetPropertyAccessPath(propertyFunc);

			FluentStringProperty<T> returnValue = new FluentStringProperty<T>(this, propertyName);

			ruleBuilder.SetValueProvider(returnValue);

			dependentProperties.Add(propertyName);

			return returnValue;
		}

		public IFluentRule<T> Value<TValue>(TValue compareValue)
		{
			ruleBuilder.SetValueProvider(new FluentValue<T>(compareValue));

			return this;
		}

		public IFluentRule<T> Value(Func<T, object> compareValue)
		{
			return this;
		}

		public IFluentRule<T> Set<TValue>(params TValue[] paramValues)
		{
			return this;
		}

		public IFluentRule<T> Set<TProp>(params Expression<Func<T, TProp>>[] paramValues)
		{
			List<string> paths = new List<string>();

			foreach (Expression<Func<T, TProp>> expression in paramValues)
			{
				string propertyName = reflectionService.GetPropertyAccessPath(expression);

				dependentProperties.Add(propertyName);

				paths.Add(propertyName);
			}

			ruleBuilder.SetValueProvider(new FluentPropertySetValue<T, TProp>(paths));

			return this;
		}

		public IFluentRule<T> Count<TProp>(Expression<Func<T, TProp>> countFunc)
		{
			return this;
		}

		public IFluentRule<T> IsWarning()
		{
			return this;
		}

		public IFluentRule<T> Message(string message)
		{
			return this;
		}

		public IFluentRule<T> Standard(string standard)
		{
			standards.Add(standard);

			return this;
		}

		#endregion

		public void Compile(ResourceLoader fluentResources)
		{
			validateAction = ruleBuilder.Compile(fluentResources);

			ruleBuilder.Clear();
			ruleBuilder = null;
		}
	}
}